package com.elitel.frame.base.service.impl;

import com.elitel.common.utils.RedisUtils;
import com.elitel.frame.base.component.RedisCacheProjectService;
import com.elitel.frame.base.component.SysReqLogKeys;
import com.elitel.frame.base.component.exportprojectdata.DataBaseConnInfo;
import com.elitel.frame.base.service.SysPrjServiceService;
import com.elitel.frame.main.dao.AcAppServiceMapper;
import com.elitel.frame.main.dao.SysPrjserviceDelMapper;
import com.elitel.frame.main.dao.SysPrjserviceMapper;
import com.elitel.frame.main.dao.ext.DbSearchMap;
import com.elitel.frame.main.dao.ext.ExtSysPrjserviceMapper;
import com.elitel.frame.main.entity.DataSourceType;
import com.elitel.frame.main.entity.SysPrjservice;
import com.elitel.frame.main.entity.ext.PrjServiceExt;
import com.elitel.frame.main.entity.vo.PageRequestVo;
import com.elitel.frame.main.entity.vo.PageResponseVo;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

import static com.elitel.frame.base.service.impl.SysReqLogServiceImpl.FREQUENCY;

@Service
public class SysPrjServiceServiceImpl implements SysPrjServiceService {

    private final Logger logger = LoggerFactory.getLogger(SysPrjServiceServiceImpl.class);

    @Autowired
    private SysPrjserviceMapper sysPrjserviceMapper;
    @Autowired
    private SysReqLogKeys sysReqLogKeys;

    @Autowired
    private SysPrjserviceDelMapper prjserviceDelMapper;
    @Autowired
    private ExtSysPrjserviceMapper extSysPrjserviceMapper;
    @Autowired
    private RedisCacheProjectService redisCacheProjectService;
    @Autowired
    private DataBaseConnInfo dataBaseConnInfo;

    @Autowired
    private AcAppServiceMapper acAppServiceMapper;


    private final long sevenDays = 7 * 24 * 60 * 60 * 1000;


    /**
     * 服务编码是否存在
     *
     * @param guid
     * @param serviceCode
     * @return
     */
    public boolean serviceCodeExist(String guid, String serviceCode) {
        List<PrjServiceExt> list = sysPrjserviceMapper.selectNeGuidEqServiceCode(guid, serviceCode);
        return list != null && !list.isEmpty();
    }

    /**
     * 服务PATH是否存在
     *
     * @param apiPath
     * @return
     */
    public boolean selectServiceApiCode(String apiPath) {
        List<PrjServiceExt> list = sysPrjserviceMapper.selectServiceApiCode(apiPath);
        return list != null && !list.isEmpty();
    }

    public Object getCounts333() {
        List<String> fSql = extSysPrjserviceMapper.getFSql();

        return fSql;
    }


    @Override
    public Map<String, Integer> getCounts(String groupId, String servType, String dataType,
                                          String servName, String dbK,
                                          String app, String tbName, Integer status
    ) {
        if ("1".equals(servType)) {
            servType = "'singleTableServer'";
        }
        if ("2".equals(servType)) {
            servType = "'executeSqlServer'";
        }
        if ("3".equals(servType)) {
            servType = "'singleTableServer','executeSqlServer'";
        }
        String sim = null;
        if ("4".equals(servType)) {
            servType = "'simServer'";
            sim = "sim";
        }
        if ("5".equals(servType)) {
            servType = "'polymerServer'";
        }
        if ("6".equals(servType)) {
            servType = "'fileServer'";
        }
        List<Map<String, Object>> list = extSysPrjserviceMapper.getCounts(groupId, servName, dbK, servType,
                dataType, sim, app, tbName, status);
        Map<String, Integer> retn = new HashMap<>();
        int dbNo = 0;
        int fileNo = 0;
        int simNo = 0;
        int aggreNo = 0;
        for (Map<String, Object> map : list) {
            String servicecode = (String) map.get("guid");
            Long count = (Long) map.get("count");
            Long count2 = (Long) map.get("COUNT");
            if (count==null || count2!=null) {
                count =count2;
            }
            int tmp = Math.toIntExact(count);
            if ("fileServer".equals(servicecode)) {
                fileNo = tmp;
            }
            if ("simServer".equals(servicecode)) {
                simNo = tmp;
            }
            if ("polymerServer".equals(servicecode)) {
                aggreNo = tmp;
            }
            if ("executeSqlServer".equals(servicecode)) {
                dbNo += tmp;
            }
            if ("singleTableServer".equals(servicecode)) {
                dbNo += tmp;
            }
        }
        retn.put("file", fileNo);
        retn.put("sim", simNo);
        retn.put("aggre", aggreNo);
        retn.put("db", dbNo);
        return retn;
    }

    @Override
    public PageResponseVo<DbSearchMap> pagePrjServiceData(Integer pageNo, Integer pageSize, String groupId,
                                                          String servType, String dataType, String servName,
                                                          String app, String dbK, String order, String tbName,
                                                          Integer status) {
        if (pageNo == null) pageNo = 1;
        if (pageSize == null) pageSize = 10;
        PageResponseVo<DbSearchMap> responseVo = new PageResponseVo<>();
        if ("1".equals(servType)) {
            servType = "'singleTableServer'";
        }
        if ("2".equals(servType)) {
            servType = "'executeSqlServer'";
        }
        if ("3".equals(servType)) {
            servType = "'singleTableServer','executeSqlServer'";
        }
        String sim = null;
        if ("4".equals(servType)) {
            servType = "'simServer'";
            sim = "sim";
        }
        if ("5".equals(servType)) {
            servType = "'polymerServer'";
        }
        if ("6".equals(servType)) {
            servType = "'fileServer'";
        }
        if (order == null || order.isEmpty()) {
            order = "desc";
        }
        PageHelper.startPage(pageNo, pageSize);
        List<DbSearchMap> dbSearchMaps = extSysPrjserviceMapper.pagePrjServiceData(
                groupId, servType, dataType, servName,
                dbK, app, sim, order, tbName, status);
        PageInfo<DbSearchMap> pageInfo = new PageInfo<>(dbSearchMaps);
        List<DbSearchMap> retn = new ArrayList<>();
        for (DbSearchMap dbSearchMap : dbSearchMaps) {
            String guid1 = dbSearchMap.getGuid();
            List<Map<String, Object>> list = extSysPrjserviceMapper.getApp(guid1);
            StringBuilder appName = new StringBuilder();
            StringBuilder appCode = new StringBuilder();
            for (Map<String, Object> acApp : list) {
                if (acApp != null) {
                    if (acApp.get("name") != null) appName.append(",").append(acApp.get("name"));
                    if (acApp.get("code") != null) appCode.append(",").append(acApp.get("code"));
                }

            }
            if (appName.length() > 0) dbSearchMap.setAppName(appName.substring(1));
            if (appCode.length() > 0) dbSearchMap.setAppCode(appCode.substring(1));
            retn.add(dbSearchMap);
        }
        responseVo.setRows(retn);
        responseVo.setTotal(Math.toIntExact(pageInfo.getTotal()));
        setExeStatusAndCounts2(dbSearchMaps);
        return responseVo;
    }

    private void setExeStatusAndCounts2(List<DbSearchMap> list) {
        Date now = new Date();
        Calendar nowInst = Calendar.getInstance();
        nowInst.setTime(now);
        long nowInMillis = nowInst.getTimeInMillis();
        SimpleDateFormat datetimeFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for (DbSearchMap item : list) {
            Map<Object, Object> successMap = RedisUtils.hGetAll(sysReqLogKeys.getSuccess() + item.getServicecode());
            Map<Object, Object> errorMap = RedisUtils.hGetAll(sysReqLogKeys.getError() + item.getServicecode());
            item.setExeStatus("正常");
            Date successDate = null;
            if (!successMap.isEmpty()) {
                successDate = getMaxDate(successMap.values(), datetimeFmt);
            }
            Date errorDate = null;
            if (!errorMap.isEmpty()) {
                errorDate = getMaxDate(errorMap.values(), datetimeFmt);
            }

            Date biggerDate;
            if (successDate != null && errorDate != null) {
                if (!successDate.after(errorDate)) {
                    item.setExeStatus("异常");
                }
                biggerDate = successDate.after(errorDate) ? successDate : errorDate;
            } else if (successDate == null) {
                biggerDate = errorDate;
            } else {
                biggerDate = successDate;
            }
            if (biggerDate == null) {
                item.setLastTm("未使用");
            } else {
                // 获取 biggerDate 的 时间戳
                Calendar instance = Calendar.getInstance();
                instance.setTime(biggerDate);
                long timeInMillis = instance.getTimeInMillis();
                if (nowInMillis - timeInMillis > sevenDays) {
                    item.setLastTm("7天未使用（" + datetimeFmt.format(biggerDate) + "）");
                } else {
                    item.setLastTm(datetimeFmt.format(biggerDate));
                }
            }

        }
    }


    @Override
    public PageResponseVo<SysPrjservice> getSysProjectServiceList(PageRequestVo<SysPrjservice> pr) {
        String databaseType = dataBaseConnInfo.dataBaseType;
        List<SysPrjservice> list = new ArrayList<SysPrjservice>();
        PageResponseVo<SysPrjservice> responseVo = new PageResponseVo<SysPrjservice>();
        switch (databaseType) {
            case "oracle":
            case DataSourceType.DM:
                pr.StartPage();
                list = extSysPrjserviceMapper.getProjectServiceByPagination(pr);
                break;
            case "mysql":
            case "h2":
                pr.setPageNo((pr.getPageNo() - 1) * pr.getPageSize());
                list = extSysPrjserviceMapper.getProjectServiceByPaginationMysql(pr);
                break;
            case "sqlite":
                pr.setPageNo((pr.getPageNo() - 1) * pr.getPageSize());
                list = extSysPrjserviceMapper.getProjectServiceByPaginationSqlite(pr);
                break;
            case DataSourceType.POSTGRES:
                pr.setPageNo((pr.getPageNo() - 1) * pr.getPageSize());
                list = extSysPrjserviceMapper.getProjectServiceByPaginationPG(pr);
                break;
        }

        Long count = 0L;
        if (databaseType.equals("sqlite")) {
            count = extSysPrjserviceMapper.getProjectServiceCountSqlite(pr);
        } else {
            count = extSysPrjserviceMapper.getProjectServiceCount(pr);
        }

        List<Map> appcountList = acAppServiceMapper.getGuidCountsOfApp();
        for (Object item1 : list) {
            PrjServiceExt item = (PrjServiceExt) item1;
            for (Map map : appcountList) {
                if (map.get("guid").equals(item.getGuid())) {
                    item.setCounts(map.get("counts").toString());
                    item.setAppNames(map.get("app_name") + "");
                }
            }
        }
        responseVo.setRows(list);
        responseVo.setTotal(count.intValue());

        // 设置运行状态和今日调用次数
        setExeStatusAndCounts(list);
        return responseVo;
    }

    private void setExeStatusAndCounts(List<SysPrjservice> list) {
        Date now = new Date();
        Calendar nowInst = Calendar.getInstance();
        nowInst.setTime(now);
        long nowInMillis = nowInst.getTimeInMillis();
        SimpleDateFormat datetimeFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for (Object item1 : list) {
            PrjServiceExt item = (PrjServiceExt) item1;
            Map<Object, Object> successMap = RedisUtils.hGetAll(sysReqLogKeys.getSuccess() + item.getServicecode());
            Map<Object, Object> errorMap = RedisUtils.hGetAll(sysReqLogKeys.getError() + item.getServicecode());
            item.setExeStatus("正常");
            Date successDate = null;
            if (!successMap.isEmpty()) {
                successDate = getMaxDate(successMap.values(), datetimeFmt);
            }
            Date errorDate = null;
            if (!errorMap.isEmpty()) {
                errorDate = getMaxDate(errorMap.values(), datetimeFmt);
            }

            Date biggerDate;
            if (successDate != null && errorDate != null) {
                if (!successDate.after(errorDate)) {
                    item.setExeStatus("异常");
                }
                biggerDate = successDate.after(errorDate) ? successDate : errorDate;
            } else if (successDate == null) {
                biggerDate = errorDate;
            } else {
                biggerDate = successDate;
            }
            if (biggerDate == null) {
                item.setLastTm("未使用");
            } else {
                // 获取 biggerDate 的 时间戳
                Calendar instance = Calendar.getInstance();
                instance.setTime(biggerDate);
                long timeInMillis = instance.getTimeInMillis();
                if (nowInMillis - timeInMillis > sevenDays) {
                    item.setLastTm("7天未使用（" + datetimeFmt.format(biggerDate) + "）");
                } else {
                    item.setLastTm(datetimeFmt.format(biggerDate));
                }
            }

        }
    }


    private Date getMaxDate(Collection<Object> dateSet, SimpleDateFormat simpleDateFormat) {
        AtomicReference<Date> date = new AtomicReference<>();
        dateSet.stream()
                .map(dateStr -> parse(simpleDateFormat, (dateStr + "").split(FREQUENCY)[1]))
                .filter(Objects::nonNull).max(Date::compareTo)
                .ifPresent(date::set);
        return date.get();
    }


    public Date parse(SimpleDateFormat simpleDateFormat, String dateStr) {
        try {
            return simpleDateFormat.parse(dateStr);
        } catch (ParseException e) {
            return null;
        }
    }


    @Override
    public void savePrjInfo(SysPrjservice sysPrjservice) {
        SysPrjservice projectConfigByGuid = getProjectConfigByGuid(sysPrjservice.getGuid());
        if (projectConfigByGuid == null) {
            String servicecode = sysPrjservice.getServicecode();
            if (StringUtils.isEmpty(servicecode)) {
                throw new RuntimeException("服务编码不能为空");
            }
            if (serviceCodeExist(sysPrjservice.getGuid(), servicecode)) {
                throw new RuntimeException("服务编码已存在，请重新填写！");
            }
            if (selectServiceApiCode(sysPrjservice.getApiPath())) {
                throw new RuntimeException("服务调用地址已存在，请重新填写！");
            }

            sysPrjservice.setCreatedate(new Date());
            sysPrjservice.setStatus(0);
            insertProjectConfig(sysPrjservice);
        } else {
            // code不能更新
            sysPrjservice.setServicecode(null);
            updateProjectConfig(sysPrjservice);
        }
    }

    /**
     * 根据主键删除数据
     *
     * @param guidList
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int delByGuids(List<String> guidList) {

        if (CollectionUtils.isEmpty(guidList)) {
            return 0;
        }
        List<SysPrjservice> list = extSysPrjserviceMapper.selectByGuIds(guidList);
        if (CollectionUtils.isEmpty(list)) {
            return 0;
        }

        int delInsertNum = 0;
        for (SysPrjservice sysPrjservice : list) {
            delInsertNum += prjserviceDelMapper.insertSelective(sysPrjservice);
        }

        // 删除数据 先不用管缓存 现在没有启动缓存
        int delNum = extSysPrjserviceMapper.delByGuids(guidList);
        if (delInsertNum != delNum) {
            throw new RuntimeException("删除失败");
        }
        // 删除分组关系
        extSysPrjserviceMapper.delGroupRelByServerIds(guidList);
        return delNum;
    }

    @Override
    public int getCount() {
        return sysPrjserviceMapper.getCount();
    }

    @Override
    public void insertProjectConfig(SysPrjservice sysPrjservice) {
        sysPrjservice.setGuid(UUID.randomUUID().toString());
        sysPrjserviceMapper.insertSelective(sysPrjservice);
        redisCacheProjectService.freshProjectServerTree();

    }

    @Override
    public SysPrjservice getProjectConfigByGuid(String guid) {
        return sysPrjserviceMapper.selectByPrimaryKey(guid);
    }

    @Override
    public void updateProjectConfig(SysPrjservice sysPrjservice) {
        sysPrjserviceMapper.updateByPrimaryKeySelective(sysPrjservice);
        redisCacheProjectService.freshProjectServerTree();

    }

    @Override
    public void deleteProjectConfig(String guids) {
        List<String> list = new ArrayList<String>();
        String[] idArray = guids.split(",");
        for (int i = 0; i < idArray.length; i++) {
            list.add(idArray[i]);
        }
        extSysPrjserviceMapper.deleteProjectServiceConfig(list);
        redisCacheProjectService.freshProjectServerTree();

    }

    @Override
    public List<SysPrjservice> getProjectServiceByPcode(String pcode) {
        return extSysPrjserviceMapper.getProjectServiceByPcode(pcode);
    }

    @Override
    public List<PrjServiceExt> getServerByProjctId(String pcode) {
        return extSysPrjserviceMapper.getServerByPcode(pcode);
    }

    @Override
    public List<PrjServiceExt> getServiceBytype(String pcode, String servertype) {
        return extSysPrjserviceMapper.getServiceBytype(pcode, servertype);
    }

    @Override
    public SysPrjservice getSysPrjserviceByServiceCode(String serviceCode) {
        return extSysPrjserviceMapper.getProjectServiceByServiceCode(serviceCode);
    }

    @Override
    public SysPrjservice getByServiceCode(String serviceCode) {
        List<SysPrjservice> byServiceCode = extSysPrjserviceMapper.geServiceList(Collections.singletonList(serviceCode), null);
        if (CollectionUtils.isEmpty(byServiceCode)) return null;
        return byServiceCode.get(0);
    }

    @Override
    public SysPrjservice getByServiceGuid(String guids) {
        List<SysPrjservice> byServiceCode = extSysPrjserviceMapper.geServiceList(null, Collections.singletonList(guids));
        if (CollectionUtils.isEmpty(byServiceCode)) return null;
        return byServiceCode.get(0);
    }

    @Override
    public List<SysPrjservice> getByServiceCodes(List<String> serviceCodes) {
        List<SysPrjservice> byServiceCode = extSysPrjserviceMapper.geServiceList(serviceCodes, null);
        if (CollectionUtils.isEmpty(byServiceCode)) return new ArrayList<>();
        return byServiceCode;
    }

    @Override
    public List<Map<String, Object>> selectCountGroupByType() {
        return extSysPrjserviceMapper.selectCountGroupByType();
    }


    @Override
    public int deleteByGuid(String guid) {
        return sysPrjserviceMapper.deleteByPrimaryKey(guid);
    }

    public PrjServiceExt getPrjServiceExt(String serviceCode) {
        PrjServiceExt prjServiceExt = sysPrjserviceMapper.selectByServiceCode(serviceCode);
        prjServiceExt.setResCvsJs(prjServiceExt.getResCvsJs());
        prjServiceExt.setApiPath(prjServiceExt.getApiPath());
        prjServiceExt.setCacheDur(prjServiceExt.getCacheDur());
        return prjServiceExt;
    }

}
